using System.IO;
using RobotBattle2.Scripting;
using System.Collections.Generic;
using RobotBattle2.Utilities;



namespace RobotBattle2.Rsl2 {


using System;
using System.CodeDom.Compiler;

internal partial class Parser {
	public const int _EOF = 0;
	public const int _identifier = 1;
	public const int _number = 2;
	public const int _string = 3;
	public const int _lparen = 4;
	public const int _rparen = 5;
	public const int maxT = 47;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

public Script Script { get; private set; }

	private Decimal ParseNumber(string text) {
		return Decimal.Parse(text);
	}

	private string ParseString(string text) {
		return text
			.Substring(1, text.Length - 2)
			.Unescape();
	}

	private bool IsFunctionCall() {
		try {
			return (la.kind == _identifier && scanner.Peek().kind == _lparen);
		} finally {
			scanner.ResetPeek();
		}
	}



	public Parser(Scanner scanner) {
		this.scanner = scanner;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}

	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	void Grammar() {
		this.Script = new Script(); 
		MethodDeclaration funcdecl; 
		MethodDeclaration(out funcdecl);
		this.Script.Methods.Add(funcdecl); 
		while (la.kind == 1) {
			MethodDeclaration(out funcdecl);
			this.Script.Methods.Add(funcdecl); 
		}
	}

	void MethodDeclaration(out MethodDeclaration funcdecl) {
		funcdecl = new MethodDeclaration(); 
		Expect(1);
		funcdecl.Name = t; 
		if (la.kind == 4) {
			ExpectWeak(4, 1);
			if (la.kind == 1) {
				Get();
				funcdecl.Parameters.Add(t); 
				while (WeakSeparator(6,2,3) ) {
					Expect(1);
					funcdecl.Parameters.Add(t); 
				}
			}
			ExpectWeak(5, 4);
		}
		Expect(7);
		StatementList(funcdecl.Statements);
		Expect(8);
	}

	void StatementList(StatementCollection list) {
		while (StartOf(5)) {
			Statement stmt; 
			Statement(out stmt);
			list.Add(stmt); 
		}
	}

	void Statement(out Statement stmt) {
		stmt = null; 
		switch (la.kind) {
		case 13: {
			VariableDeclarationStatement(out stmt);
			break;
		}
		case 14: {
			IfStatement(out stmt);
			break;
		}
		case 18: {
			WhileLoopStatement(out stmt);
			break;
		}
		case 21: {
			ReturnStatement(out stmt);
			break;
		}
		case 9: {
			Get();
			ExpectWeak(10, 6);
			stmt = new NextLoopStatement(); 
			break;
		}
		case 11: {
			Get();
			ExpectWeak(10, 6);
			stmt = new BreakLoopStatement(); 
			break;
		}
		case 1: case 2: case 3: case 4: case 33: case 34: case 39: case 40: case 41: case 42: case 43: {
			Expression expr; 
			Expression(out expr);
			stmt = new ExpressionStatement(expr); 
			if (la.kind == 12) {
				Get();
				Expression value; 
				Expression(out value);
				stmt = new AssignmentStatement(expr, value); 
			}
			ExpectWeak(10, 6);
			break;
		}
		default: SynErr(48); break;
		}
	}

	void VariableDeclarationStatement(out Statement stmt) {
		Expect(13);
		var vardeclstmt = new VariableDeclarationStatement(); 
		VariableDeclaration(vardeclstmt.Variables);
		stmt = vardeclstmt; 
		while (WeakSeparator(6,2,7) ) {
			VariableDeclaration(vardeclstmt.Variables);
		}
		ExpectWeak(10, 6);
	}

	void IfStatement(out Statement stmt) {
		Expect(14);
		var ifStmt = new IfStatement(); 
		stmt = ifStmt; 
		Expect(4);
		Expression condition; 
		Expression(out condition);
		ifStmt.Condition = condition; 
		Expect(5);
		StatementList(ifStmt.TrueStatements);
		while (la.kind == 15) {
			Get();
			var elseIfStmt = new IfStatement(); 
			Expect(4);
			Expression(out condition);
			elseIfStmt.Condition = condition; 
			Expect(5);
			ifStmt.FalseStatements.Add(elseIfStmt); 
			StatementList(elseIfStmt.TrueStatements);
			ifStmt = elseIfStmt; 
		}
		if (la.kind == 16) {
			Get();
			StatementList(ifStmt.FalseStatements);
		}
		Expect(17);
	}

	void WhileLoopStatement(out Statement stmt) {
		Expect(18);
		var whileStmt = new WhileLoopStatement(); 
		stmt = whileStmt; 
		Expect(4);
		Expression condition; 
		Expression(out condition);
		whileStmt.Condition = condition; 
		Expect(5);
		StatementList(whileStmt.Statements);
		if (la.kind == 19) {
			Get();
		} else if (la.kind == 20) {
			Get();
		} else SynErr(49);
	}

	void ReturnStatement(out Statement stmt) {
		stmt = null; 
		Expect(21);
		if (StartOf(8)) {
			Expression value; 
			Expression(out value);
			stmt = new ReturnStatement(value); 
		} else if (la.kind == 10) {
			stmt = new ReturnStatement(); 
		} else SynErr(50);
		ExpectWeak(10, 6);
	}

	void Expression(out Expression expr) {
		expr = null; 
		LogicalAndExpression(out expr);
		Expression right; 
		while (la.kind == 22 || la.kind == 23) {
			if (la.kind == 22) {
				Get();
				LogicalAndExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Or); 
			} else {
				Get();
				LogicalAndExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Or); 
			}
		}
	}

	void VariableDeclaration(IList<VariableDeclaration> list) {
		Expect(1);
		var name = t; 
		Expression init = null; 
		if (la.kind == 12) {
			Get();
			Expression(out init);
		}
		list.Add(new VariableDeclaration(name, init)); 
	}

	void LogicalAndExpression(out Expression expr) {
		expr = null; 
		EqualityExpression(out expr);
		Expression right; 
		while (la.kind == 24 || la.kind == 25) {
			if (la.kind == 24) {
				Get();
				EqualityExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.And); 
			} else {
				Get();
				EqualityExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.And); 
			}
		}
	}

	void EqualityExpression(out Expression expr) {
		expr = null; 
		RelationalExpression(out expr);
		Expression right; 
		while (la.kind == 26 || la.kind == 27 || la.kind == 28) {
			if (la.kind == 26) {
				Get();
				RelationalExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.NotEqual); 
			} else if (la.kind == 27) {
				Get();
				RelationalExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.NotEqual); 
			} else {
				Get();
				RelationalExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Equal); 
			}
		}
	}

	void RelationalExpression(out Expression expr) {
		expr = null; 
		AdditiveExpression(out expr);
		Expression right; 
		while (StartOf(9)) {
			if (la.kind == 29) {
				Get();
				AdditiveExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.GreaterEqual); 
			} else if (la.kind == 30) {
				Get();
				AdditiveExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Greater); 
			} else if (la.kind == 31) {
				Get();
				AdditiveExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Less); 
			} else {
				Get();
				AdditiveExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.LessEqual); 
			}
		}
	}

	void AdditiveExpression(out Expression expr) {
		expr = null; 
		MultiplicativeExpression(out expr);
		Expression right; 
		while (la.kind == 33 || la.kind == 34) {
			if (la.kind == 33) {
				Get();
				MultiplicativeExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Add); 
			} else {
				Get();
				MultiplicativeExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Subtract); 
			}
		}
	}

	void MultiplicativeExpression(out Expression expr) {
		expr = null; 
		ExponentialExpression(out expr);
		Expression right; 
		while (la.kind == 35 || la.kind == 36 || la.kind == 37) {
			if (la.kind == 35) {
				Get();
				ExponentialExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Multiply); 
			} else if (la.kind == 36) {
				Get();
				ExponentialExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Divide); 
			} else {
				Get();
				ExponentialExpression(out right);
				expr = new BinaryExpression(expr, right, BinaryOperator.Modulo); 
			}
		}
	}

	void ExponentialExpression(out Expression expr) {
		expr = null; 
		UnaryExpression(out expr);
		Expression right; 
		if (la.kind == 38) {
			Get();
			ExponentialExpression(out right);
			expr = new BinaryExpression(expr, right, BinaryOperator.Multiply); 
		}
	}

	void UnaryExpression(out Expression expr) {
		expr = null; 
		if (la.kind == 33) {
			Get();
			AtomicExpression(out expr);
			expr = new UnaryExpression(expr, UnaryOperator.Plus); 
		} else if (la.kind == 34) {
			Get();
			AtomicExpression(out expr);
			expr = new UnaryExpression(expr, UnaryOperator.Minus); 
		} else if (la.kind == 39) {
			Get();
			AtomicExpression(out expr);
			expr = new UnaryExpression(expr, UnaryOperator.Not); 
		} else if (StartOf(10)) {
			AtomicExpression(out expr);
		} else SynErr(51);
	}

	void AtomicExpression(out Expression expr) {
		expr = null; 
		if (la.kind == 4) {
			Get();
			Expression(out expr);
			Expect(5);
		} else if (la.kind == 2) {
			Get();
			expr = new ConstantExpression(t, ParseNumber(t.val)); 
		} else if (la.kind == 3) {
			Get();
			expr = new ConstantExpression(t, ParseString(t.val)); 
		} else if (la.kind == 40) {
			Get();
			expr = new ConstantExpression(t, true); 
		} else if (la.kind == 41) {
			Get();
			expr = new ConstantExpression(t, false); 
		} else if (la.kind == 42) {
			Get();
			expr = new ConstantExpression(t, true); 
		} else if (la.kind == 43) {
			Get();
			expr = new ConstantExpression(t, false); 
		} else if (IsFunctionCall()) {
			InvokeExpression(out expr);
		} else if (la.kind == 1) {
			VariableExpression(out expr);
		} else SynErr(52);
	}

	void InvokeExpression(out Expression expr) {
		Expect(1);
		var invoke = new InvokeExpression(t); expr=invoke; 
		Expect(4);
		Expression p; 
		if (StartOf(8)) {
			Expression(out p);
			invoke.Parameters.Add(p); 
			while (WeakSeparator(6,8,3) ) {
				Expression(out p);
				invoke.Parameters.Add(p); 
			}
		}
		Expect(5);
	}

	void VariableExpression(out Expression expr) {
		Expect(1);
		expr = new VariableExpression(t); 
		while (la.kind == 44 || la.kind == 45) {
			if (la.kind == 44) {
				Get();
				Expect(1);
				expr = new PropertyExpression(expr, t); 
			} else {
				Get();
				Expression index; 
				Expression(out index);
				expr = new IndexerExpression(expr, index); 
				Expect(46);
			}
		}
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		Grammar();

		Expect(0);
	}
	
	bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{T,T,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{T,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,T,T,T, T,x,x,x, x,T,x,T, x,T,T,x, x,x,T,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,x,x,T, T,T,T,T, x,x,x,x, x},
		{T,T,T,T, T,x,x,x, T,T,x,T, x,T,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,x,x,T, T,T,T,T, x,x,x,x, x},
		{x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,x,x,T, T,T,T,T, x,x,x,x, x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x}

	};
	
	
	void SynErr (int n) {
		if (errDist >= minErrDist) {
			string s;
			switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "identifier expected"; break;
			case 2: s = "number expected"; break;
			case 3: s = "string expected"; break;
			case 4: s = "lparen expected"; break;
			case 5: s = "rparen expected"; break;
			case 6: s = "\",\" expected"; break;
			case 7: s = "\"{\" expected"; break;
			case 8: s = "\"}\" expected"; break;
			case 9: s = "\"next\" expected"; break;
			case 10: s = "\";\" expected"; break;
			case 11: s = "\"break\" expected"; break;
			case 12: s = "\"=\" expected"; break;
			case 13: s = "\"var\" expected"; break;
			case 14: s = "\"if\" expected"; break;
			case 15: s = "\"elseif\" expected"; break;
			case 16: s = "\"else\" expected"; break;
			case 17: s = "\"endif\" expected"; break;
			case 18: s = "\"while\" expected"; break;
			case 19: s = "\"endwhile\" expected"; break;
			case 20: s = "\"endw\" expected"; break;
			case 21: s = "\"return\" expected"; break;
			case 22: s = "\"or\" expected"; break;
			case 23: s = "\"||\" expected"; break;
			case 24: s = "\"and\" expected"; break;
			case 25: s = "\"&&\" expected"; break;
			case 26: s = "\"<>\" expected"; break;
			case 27: s = "\"!=\" expected"; break;
			case 28: s = "\"==\" expected"; break;
			case 29: s = "\">=\" expected"; break;
			case 30: s = "\">\" expected"; break;
			case 31: s = "\"<\" expected"; break;
			case 32: s = "\"<=\" expected"; break;
			case 33: s = "\"+\" expected"; break;
			case 34: s = "\"-\" expected"; break;
			case 35: s = "\"*\" expected"; break;
			case 36: s = "\"/\" expected"; break;
			case 37: s = "\"%\" expected"; break;
			case 38: s = "\"^\" expected"; break;
			case 39: s = "\"!\" expected"; break;
			case 40: s = "\"_true\" expected"; break;
			case 41: s = "\"_false\" expected"; break;
			case 42: s = "\"_on\" expected"; break;
			case 43: s = "\"_off\" expected"; break;
			case 44: s = "\".\" expected"; break;
			case 45: s = "\"[\" expected"; break;
			case 46: s = "\"]\" expected"; break;
			case 47: s = "??? expected"; break;
			case 48: s = "invalid Statement"; break;
			case 49: s = "invalid WhileLoopStatement"; break;
			case 50: s = "invalid ReturnStatement"; break;
			case 51: s = "invalid UnaryExpression"; break;
			case 52: s = "invalid AtomicExpression"; break;

				default: s = "error " + n; break;
			}
			Error(la.filename, la.line, la.col, n, s);
		}
		errDist = 0;
	}

	void Error(string filename, int line, int column, int errorCode, string message) {
		Script.Errors.Add(
			new CompilerError(
				filename, line, column,
				string.Format("RSL{0:d4}", errorCode),
				message
			) { IsWarning = false }
		);
	}

} // end Parser

}